/*********************************************************************************
    L1 cache unit for PRV5xx processor
    //input cache interface
    //output BIU interface
    // 128Bit I/O 
    // |-----------------------------------------|-----------(IN ENTRY)------------|
    // |----------TAG(29Bit)---|---INDEX(5Bit)---|---LINE(2Bit)---|---DUMY(4bit)---|
*********************************************************************************/

//TODO SIZE LOGIC FROM PIPELINE TO BUS

`include "PRV564Config.v"
`include "PRV564Define.v"
module L1I
#(
    parameter   SRAM_BANKCOEFF=2,
                SRAM_BANKNUM=2**SRAM_BANKCOEFF,
                CACHE_DEPTH=SRAM_BANKNUM*64,
                WAY_NUM=2,
                INDEX_DEPTH=CACHE_DEPTH/WAY_NUM/4,
                CACHE_FIBID=8'h01
)
(
    input  wire             GLB_CLKi,
    input  wire             GLB_RSTi,                      //Cache冲刷请求
    //Access Queue
    input  wire             AQ_V,
    input  wire [7:0]       AQ_ID,
    input  wire [7:0]       AQ_CMD,
    input  wire [15:0]      AQ_BSEL,
    input  wire             AQ_CI,
    input  wire             AQ_WT,
    input  wire [127:0]     AQ_WDATA,
    input  wire [`PADR-1:0] AQ_ADDR,
    output wire             AQ_FULL,
    //Respond Queue
    output reg              RQ_V,
    output reg  [7:0]       RQ_ID,
    output wire             RQ_WRERR,
    output wire             RQ_RDERR,
    output wire             RQ_RDY,
    output wire [127:0]     RQ_RDATA,
    input  wire             RQ_ACK,
    //To FIB
    output L1_FIBo_REQ,
    output L1_FIBo_WREN,
    output [7:0] L1_FIBo_ID,
    output [7:0] L1_FIBo_CMD,
    output [3:0] L1_FIBo_BURST,
    output [3:0] L1_FIBo_SIZE,
    output [`PADR-1:0] L1_FIBo_ADDR,
    output [`XLEN-1:0] L1_FIBo_DATA,
    // slave reply
    input L1_FIBi_ACK,
    input L1_FIBi_FULL,
    input L1_FIBi_V,
    input[7:0] L1_FIBi_ID,
    input[7:0] L1_FIBi_RPL,
    input[`XLEN-1:0] L1_FIBi_DATA
    
);
localparam INDEX_WIDTH=$clog2(INDEX_DEPTH);
localparam TAG_WIDTH=`PADR-INDEX_WIDTH-6;
localparam MEM_ADDRWID=$clog2(CACHE_DEPTH);
localparam WAYADDR_WID = $clog2(WAY_NUM);

localparam FSM_IDLE = 3'h0;//Normal Operation
localparam FSM_FILL = 3'h1;//bulk cache refill
localparam FSM_SGRL = 3'h2;//single read low 64b
localparam FSM_SGRH = 3'h3;//single read high 64b
localparam FSM_FNCE = 3'h4;

wire Pipe_Stall;
assign Pipe_Stall=(RQ_V & !RQ_ACK);

//AQ FIFO OUTPUT
wire             AQo_V;
wire [7:0]       AQo_ID;
wire [7:0]       AQo_CMD;
wire [15:0]      AQo_BSEL;
wire             AQo_CI;
wire             AQo_WT;
wire [127:0]     AQo_WDATA;
wire [`PADR-1:0] AQo_ADDR;
wire             AQo_FREE;
reg CMD_FINISH;
L1_AQBuf #(.L1IBUF(1'b1))
L1I_AQBuf(
    .GLB_CLKi(GLB_CLKi),
    .GLB_RSTi(GLB_RSTi),      
    .AQi_V(AQ_V),
    .AQi_ID(AQ_ID),
    .AQi_CMD(AQ_CMD),
    .AQi_BSEL(AQ_BSEL),
    .AQi_CI(AQ_CI),
    .AQi_WT(AQ_WT),
    .AQi_WDATA(AQ_WDATA),
    .AQi_ADDR(AQ_ADDR),
    .AQi_FULLo(AQ_FULL),

    .AQo_V(AQo_V),
    .AQo_ID(AQo_ID),
    .AQo_CMD(AQo_CMD),
    .AQo_BSEL(AQo_BSEL),
    .AQo_CI(AQo_CI),
    .AQo_WT(AQo_WT),
    .AQo_WDATA(AQo_WDATA),
    .AQo_ADDR(AQo_ADDR),
    .AQo_FREE(AQo_FREE)

);
assign AQo_FREE=!Pipe_Stall & CMD_FINISH;
reg [2:0]state,state_next;
reg [127:0]Cacheless_Buffer;
wire TagFlush,CacheAccess;
wire line_miss,cacheram_rdy;
//BIU 2 Cache interface
reg bus_seq_read,bus_single_read;
wire [15:0]bus_seq_cnt;
wire [63:0]bus_data_read;
wire bus_datavalid;
wire bus_finished;
wire bus_error;
wire [3:0]req_size;
wire [3:0]req_bsel;
wire [3:0]bsel_enc;
wire [127:0]bufwr_bsel;

always@(posedge GLB_CLKi or posedge GLB_RSTi)//Cache 主状态机
    if(GLB_RSTi)
        state<=FSM_IDLE;
    else
        state<=state_next;
    
always@(*)//FSM Transfer
    case(state)
    FSM_IDLE:
    begin
        if(AQo_V)  
            casez ({10{(!Pipe_Stall)}}&{AQo_CI,line_miss,AQo_CMD})
                10'b??_0000_0000:
                    state_next=FSM_IDLE;
                10'b00_0000_0010:  //Cache Normal read
                    state_next=FSM_IDLE;    
                10'b1?_0000_0010: //Cacheless read
                    if(AQo_BSEL[7:0]==0)
                        state_next=FSM_SGRH;
                    else 
                        state_next=FSM_SGRL;
                10'b01_0000_0010: //Cache miss read
                    state_next=FSM_FILL;
                {2'b??,`LSU_CacheRef}:
                    state_next=FSM_FNCE;
                default: 
                begin
                    state_next=FSM_IDLE;
                    $display("ERROR: Cache UNDEFINED INPUT ENCOUNTERED! opcode:%x",{AQo_CI,AQo_WT,line_miss,AQo_CMD});
                end
            endcase
        else
            state_next=FSM_IDLE;
    end
    FSM_FILL:
    begin
        if(bus_finished)
            state_next=FSM_IDLE;
        else 
            state_next=FSM_FILL;
    end
    FSM_SGRH:
    begin
        if(bus_finished)
            state_next=FSM_IDLE;
        else 
            state_next=FSM_SGRH;
    end
    FSM_SGRL:
    if(bus_finished)
        if(AQo_BSEL[15:8]==0)
            state_next=FSM_IDLE;
        else 
            state_next=FSM_SGRH;
    else 
        state_next=FSM_SGRL;
    FSM_FNCE:
        state_next=FSM_IDLE;
    default:
        state_next=FSM_IDLE;
    endcase
always@(posedge GLB_CLKi or posedge GLB_RSTi)//Single read req block
    if(GLB_RSTi)
        bus_single_read<=0;
    else if((state_next == FSM_SGRL) | ((state_next == FSM_SGRH) & (state !=FSM_SGRL)))
        bus_single_read<=1;
    else 
        bus_single_read<=0;
always@(*)//COMMAND FINISH BLOCK
    casez({AQo_CI,line_miss,AQo_CMD})
        {2'b0?,`LSU_READ}:
            CMD_FINISH=(state_next == FSM_IDLE) & (state == FSM_IDLE);
        {3'b1?,`LSU_READ}:
            CMD_FINISH=(state_next == FSM_IDLE);
        //{3'b???,`LSU_NOP}:
        //    CMD_FINISH=1'b1;
        default:
            CMD_FINISH=1'b1;
    endcase
always@(posedge GLB_CLKi or posedge GLB_RSTi)//Seq read req block
    if(GLB_RSTi)
        bus_seq_read<=0;
    else if(state_next == FSM_FILL)
        bus_seq_read<=1;
    else 
        bus_seq_read<=0;
reg [15:0]BSEL_Hold;
wire [127:0]CLB_WrDat;
wire CLB_WrCond;
assign CLB_WrDat=({bus_data_read,bus_data_read} & bufwr_bsel) | (Cacheless_Buffer & (~bufwr_bsel));  
assign CLB_WrCond=AQo_CI & bus_finished &((state==FSM_SGRH)|(state==FSM_SGRL));
always@(posedge GLB_CLKi)//Cacheless buffer blk
    if(CLB_WrCond)
        Cacheless_Buffer<=CLB_WrDat;
    else 
        Cacheless_Buffer<=Cacheless_Buffer;
always@(posedge GLB_CLKi)//BSEL_Hold block
    if((state_next == FSM_SGRL) | ((state_next == FSM_SGRH) & (state !=FSM_SGRL)))
        BSEL_Hold<=AQo_BSEL;
    else 
        BSEL_Hold<=BSEL_Hold;
//---------------------Cache Hit选择---------------------------------

wire [INDEX_WIDTH+3:4]index_addr;//Low word neglected
wire [TAG_WIDTH-1:0]tag_addr;
wire [1:0]entry_addr;
assign CacheAccess=(AQo_CMD==`LSU_READ) & (!AQo_CI);
assign TagFlush=(state_next==FSM_FNCE);//(AQo_CMD==`LSU_CacheRef)

assign index_addr=AQo_ADDR[INDEX_WIDTH+5:6];
assign tag_addr=AQo_ADDR[`PADR-1:INDEX_WIDTH+6];
assign entry_addr=AQo_ADDR[5:4];
wire [MEM_ADDRWID-1:0]CacheAccessAddr;
wire [MEM_ADDRWID-1:0]CacheMemWAddr;

generate if(WAY_NUM>1)
begin : WAY_ASSOCIATIVE
    wire [WAYADDR_WID-1:0]WayReplace_Addr,WayAccess_addr;
    tagman_wa#(
        .WAY_DEPTH(INDEX_DEPTH), //How many entries does 1 way have?
        .WAY_NUM(WAY_NUM),//MUST > 1
        .TAG_WID(TAG_WIDTH),
        .WBACK_ENABLE(1'b0) //this actually heavily depends on optimization
    )
    TAG_MANAGER_L1I(
        .clk(GLB_CLKi),
        .rst(GLB_RSTi),
        //Core interface
        .entry_read(CacheAccess),   
        .entry_wthru(1'b0),//NO NEED TO WRITE FOR L1I
        .entry_wback(1'b0),
        .address_tag(tag_addr),
        .address_index(index_addr),	
        .valid_clear(TagFlush), //flush
        .dfence_en(1'b0),
        //BIU interface
        .fill_finish(bus_finished), 
        .writeback_ok(1'b0),
        .line_miss(line_miss),
        .replace_dirty(),
        .dirty_comp(),
        .dirty_index(),
        .way_dirty_addr(),
        .way_replace_addr(WayReplace_Addr),
        .way_access_addr(WayAccess_addr)
    );
    assign CacheAccessAddr={WayAccess_addr,index_addr,entry_addr};
    assign CacheMemWAddr={WayReplace_Addr,index_addr,bus_seq_cnt[2:1]};
end
else
begin : DIRECT_MAPPED
    tagman_dm #(
        .WAY_DEPTH(INDEX_DEPTH),
        .TAG_WID(TAG_WIDTH),//1K per page
        .WBACK_ENABLE(1'b0) //this actually heavily depends on optimization
    )TAGMAN_DM
    (
        .clk(GLB_CLKi),
        .rst(GLB_RSTi),
        //Core interface
        .entry_read(CacheAccess),   
        .entry_wthru(1'b0),//NO NEED TO WRITE FOR L1I
        .entry_wback(1'b0),
        .address_tag(tag_addr),
        .address_index(index_addr),	
        .valid_clear(TagFlush), //flush
        .dfence_en(1'b0),

        .fill_finish(bus_finished), 
        .writeback_ok(1'b0),
        .line_miss(line_miss),
        .replace_dirty(),
        .writeback_addr(),//When D Fence, the address of valid dirty line to write back
        .writeback_index()
    );
    assign CacheAccessAddr={index_addr,entry_addr};
    assign CacheMemWAddr={index_addr,bus_seq_cnt[2:1]};
end
endgenerate

reg Cache_CI,Cache_V;
reg  [7:0]Cache_ID;
reg  [MEM_ADDRWID-1:0] CacheMemRAddr_reg,CacheMemRAddr_hold;
wire [MEM_ADDRWID-1:0]CacheMemRAddr;
assign CacheMemRAddr=(Pipe_Stall)?CacheMemRAddr_hold:CacheMemRAddr_reg;
//Memory Stage
always@(posedge GLB_CLKi or posedge GLB_RSTi)//Valid 传递 
begin
    if(GLB_RSTi)
        Cache_V <=1'b0;
    else if(Pipe_Stall)
        Cache_V <=Cache_V;    
    else 
        Cache_V <=(AQo_V & CMD_FINISH);
end
always@(posedge GLB_CLKi or posedge GLB_RSTi)//ID & CI
begin
    if(GLB_RSTi)
    begin
        Cache_ID <=0;
        Cache_CI <=0;
    end
    else if(Pipe_Stall | !(AQo_V & CMD_FINISH))//(state_next!=FSM_IDLE)!Cache_V
    begin   
        Cache_ID <=Cache_ID;
        Cache_CI <=Cache_CI;
    end
    else 
    begin
        Cache_ID <=AQo_ID;
        Cache_CI <=AQo_CI;
    end
end

always@(posedge GLB_CLKi) //Cache级地址
begin
    if( Pipe_Stall | !(AQo_V & (state_next == FSM_IDLE) & (state == FSM_IDLE)))
    begin
        CacheMemRAddr_reg  <=CacheMemRAddr_reg;
        CacheMemRAddr_hold <=CacheMemRAddr_hold;
    end
    else 
    begin   
        CacheMemRAddr_reg  <=CacheAccessAddr;
        CacheMemRAddr_hold <=CacheMemRAddr_reg;
    end
       
end


wire [127:0]CacheMemDi,CacheMemDo;
wire CacheMemWe;
wire [15:0]CacheMemBsel;

//Cache Memory interface
assign CacheMemDi={bus_data_read,bus_data_read};
assign CacheMemBsel=(bus_seq_cnt[0])?16'hff00:16'h00ff;//for cacheable case, we ALWAYS do 64 bit * 8 burst
assign CacheMemWe=(!AQo_CI)&bus_datavalid;

//SRAM ACCESS PORT
`ifdef YSYX
    wire [SRAM_BANKCOEFF-1:0]rbank_addr,wbank_addr;
    wire [5:0]sram_raddr,sram_waddr;
    assign rbank_addr=CacheMemRAddr[SRAM_BANKCOEFF+1:2];
    assign wbank_addr=CacheMemWAddr[SRAM_BANKCOEFF+1:2];
    assign sram_raddr={CacheMemRAddr[MEM_ADDRWID-1:SRAM_BANKCOEFF+2],CacheMemRAddr[1:0]};
    assign sram_waddr={CacheMemWAddr[MEM_ADDRWID-1:SRAM_BANKCOEFF+2],CacheMemWAddr[1:0]};

    pDPRAM_ysyx#(.BANK_COEFF(SRAM_BANKCOEFF))
    CACHE_MEMORY
    (
        .raddr(sram_raddr),
        .rbank(rbank_addr),//CacheAccess & (!line_miss | bus_finished)
        .rdce(1'b1),//Only when data is okay, start read(prevent R-W blocking)
        .dato(CacheMemDo),
        .rvalid(cacheram_rdy),
        .waddr(sram_waddr),
        .wbank(wbank_addr),
        .wbsel(CacheMemBsel&{16{CacheMemWe}}),
        .dati(CacheMemDi),
        .clk(GLB_CLKi),
        .rst(GLB_RSTi)
    );
`else
    cachemem#(
        .datawidth(128),
        .cache_depth(SRAM_BANKCOEFF*32)
    )CACHEMEM
    (
        .cwait(1'b0),
        .raddr(CacheMemRAddr),
        .waddr(CacheMemWAddr),
        .di(CacheMemDi),
        .we(CacheMemWe),
        .bsel(CacheMemBsel),
        .dato(CacheMemDo),
        .rclk(GLB_CLKi),
        .wclk(GLB_CLKi)
    );
    assign cacheram_rdy=1'b1;
`endif
//Finally output 
assign RQ_RDATA=(Cache_CI)?Cacheless_Buffer:CacheMemDo;
always@(posedge GLB_CLKi or posedge GLB_RSTi)
begin
    if(GLB_RSTi)
    begin
        RQ_V <=0;     
        RQ_ID    <=0;
    end
    else if(Pipe_Stall)//(state_next!=FSM_IDLE)
    begin   
        RQ_V <=RQ_V;     
        RQ_ID    <=RQ_ID;
    end
    else 
    begin  //current no istruction stall, keep on going
        RQ_V         <=Cache_V ;
        RQ_ID        <=Cache_ID;
    end
end
assign bufwr_bsel={
        {8{BSEL_Hold[15]&(state==FSM_SGRH)}},
        {8{BSEL_Hold[14]&(state==FSM_SGRH)}},
        {8{BSEL_Hold[13]&(state==FSM_SGRH)}},
        {8{BSEL_Hold[12]&(state==FSM_SGRH)}},
        {8{BSEL_Hold[11]&(state==FSM_SGRH)}},
        {8{BSEL_Hold[10]&(state==FSM_SGRH)}},
        {8{BSEL_Hold[9]&(state==FSM_SGRH)}},
        {8{BSEL_Hold[8]&(state==FSM_SGRH)}},
        {8{BSEL_Hold[7]&(state==FSM_SGRL)}},
        {8{BSEL_Hold[6]&(state==FSM_SGRL)}},
        {8{BSEL_Hold[5]&(state==FSM_SGRL)}},
        {8{BSEL_Hold[4]&(state==FSM_SGRL)}},
        {8{BSEL_Hold[3]&(state==FSM_SGRL)}},
        {8{BSEL_Hold[2]&(state==FSM_SGRL)}},
        {8{BSEL_Hold[1]&(state==FSM_SGRL)}},
        {8{BSEL_Hold[0]&(state==FSM_SGRL)}}
                };
assign bsel_enc=4'h2+`AXI_DIFF;//TODO: 
assign req_size=(bus_seq_read)?4'h3:4'h0;
assign req_bsel=(bus_seq_read)?4'h3:bsel_enc;

    // slave reply
FIB_BIU_L1I #(.FIBID(CACHE_FIBID)) BIU (
    .GLBi_CLK(GLB_CLKi),
    .GLBi_ARST(GLB_RSTi),
    .BIUo_WREN(L1_FIBo_WREN),
    .BIUo_REQ(L1_FIBo_REQ),
    .BIUo_ID(L1_FIBo_ID),
    .BIUo_CMD(L1_FIBo_CMD),
    .BIUo_BURST(L1_FIBo_BURST),
    .BIUo_SIZE(L1_FIBo_SIZE),
    .BIUo_ADDR(L1_FIBo_ADDR[`PADR-1:0]),
    .BIUo_DATA(L1_FIBo_DATA),
    .BIUi_FULL(L1_FIBi_FULL),
    .BIUi_V(L1_FIBi_V),
    .BIUi_ACK(L1_FIBi_ACK),
    .BIUi_ID(L1_FIBi_ID),
    .BIUi_RPL(L1_FIBi_RPL),
    .BIUi_DATA(L1_FIBi_DATA),
    .single_read_req(bus_single_read),			//请求读一次
    .seq_read_req(bus_seq_read),		//请求读一行
    .seq_size(req_size),
    .req_bsel(req_bsel),//TODO: SEPRATE 32b instr single access and 64*8 sequence access
    .bus_addr(AQo_ADDR & {{`PADR-6{1'b1}},{6{AQ_CI}}}),	
    .bus_rdata(bus_data_read),
    .addr_count(bus_seq_cnt),
    .data_valid(bus_datavalid),			//cache写
    .trans_error(bus_error),
    .bus_trans_finish(bus_finished)			//传输完成
);

assign RQ_RDERR= (state != FSM_IDLE)& bus_error;
assign RQ_WRERR=1'b0;
assign RQ_RDY=cacheram_rdy;
endmodule



